

#include "cmatrixprod.h"
#include <libspe2.h>
#include <malloc_align.h>

typedef struct {
  abs_params_t *params;
  spe_context_ptr_t ctx; 
} thread_block_t;


/*
 *  LANCER UN THREAD 
 */
void *ppu_pthread_function(void *arg) {
 
  uint32_t entry = SPE_DEFAULT_ENTRY;
  thread_block_t *block=(thread_block_t *)(arg);
  spe_context_ptr_t ctx=block->ctx;
  abs_params_t *abs_params=block->params; 

  if (spe_context_run(ctx, &entry, 0, abs_params, NULL, NULL) < 0) 
    perror ("Failed running context");
  pthread_exit(NULL);
}

/*  Start NUM_SPE threads  */
void start_threads(spe_context_ptr_t *spe,thread_block_t *arg, 
		   spe_program_handle_t *prog, pthread_t *thread, abs_params_t *abs_params){
  int i,ret;
  prog = spe_image_open("cmatrixprod_spe");
  if (!prog) {
    perror("spe_image_open");
    exit(1);
  }
  for (i = 0; i < NUM_SPE; i++) {

    spe[i] = spe_context_create(0, NULL);
    if (!spe[i]) {
      perror("spe_context_create");
      exit(1);
    }
    ret = spe_program_load(spe[i], prog);
    if (ret) {
      perror("spe_program_load");
      exit(1);
    }
    arg[i].params = (abs_params_t*)_malloc_align(sizeof(abs_params_t),4);
    memcpy(arg[i].params,abs_params,sizeof(abs_params_t));
    arg[i].params->spe_num = i;
    arg[i].ctx = spe[i];

    if (pthread_create (&thread[i], NULL, &ppu_pthread_function, &arg[i]))  
      perror("creation thread");
  }
}

/*  Wait thread termination & free ressources  */
void wait_free(pthread_t *thread,spe_context_ptr_t *spe, spe_program_handle_t *prog){
  int i,ret;
  for (i=0; i<NUM_SPE; i++) { 
    if (pthread_join (thread[i], NULL)) 
      perror("probleme pthread_join");
    if (spe_context_destroy (spe[i]) != 0) 
      perror("probleme destroying context");
  }
  ret = spe_image_close(prog);
  if (ret) {
    //perror("spe_image_close");
    //exit(1);
  }
}

/***************
 *  PRINCIPAL  *
 ***************/

int cmatrixprod(int M, int N, int P,t_complex *A, 
		t_complex *B,t_complex *C){
  int i,nbspe,x,y,f;
  unsigned int adrl,tmp,recv_sig, send_sig = GO;
  char* adr_bloc_buff[NUM_SPE];
  spe_context_ptr_t spe[NUM_SPE];
  pthread_t thread[NUM_SPE];
  thread_block_t arg[NUM_SPE];
  spe_program_handle_t *prog = NULL;  
  abs_params_t abs_params __attribute__((aligned(16))); 

  /*  GESTION PARAMETRES  */
  abs_params.size_M = M;
  abs_params.size_N = N;
  abs_params.size_P = P;
  abs_params.in_matrix_A = (long long)A;
  abs_params.in_matrix_B = (long long)B;
  abs_params.out_matrix_C = (long long)C;

  /*  GET NB SPE  */
  nbspe = spe_cpu_info_get(SPE_COUNT_USABLE_SPES, -1);
  if (nbspe < NUM_SPE) 
    perror("pas assez de SPE");


  /*  DEMARRAGE DES THREADS  */
  start_threads(spe, arg, prog, thread, &abs_params);


  /*  RECV SPE BUFF ADDRESSES  */
  for(i=0; i<NUM_SPE; i++){
    while (spe_out_mbox_status(spe[i])==0);    
    spe_out_mbox_read(spe[i], &adrl, 1);
    adr_bloc_buff[i]=(char*)spe_ls_area_get(spe[i])+adrl;
  }

  /*  SEND SPEi+1 BUFF ADDR TO SPEi  */
  for(i=0; i<NUM_SPE; i++){   
    tmp=((long long unsigned int)adr_bloc_buff[i]>>32);  
    while (spe_in_mbox_status(spe[(i+1)%NUM_SPE]) == 0);
    spe_in_mbox_write(spe[(i+1)%NUM_SPE], &tmp, 1,SPE_MBOX_ALL_BLOCKING);
    tmp=(unsigned int) (unsigned long long) adr_bloc_buff[i];
    while (spe_in_mbox_status(spe[(i+1)%NUM_SPE]) == 0);
    spe_in_mbox_write(spe[(i+1)%NUM_SPE], &tmp,1,SPE_MBOX_ALL_BLOCKING);
  }
  
  // HANDLE SIGNALS BETWEEN SPEs POUR VERSION SYNCHRONISEE //

  /* for(x=0; x<NUM_SPE; x++){  */
/*     for(y=0; y<NUM_SPE; y++){ */
/*       for (f=0; f<NUM_SPE; f++){ */
/* 	for(i=0; i<NUM_SPE; i++){ */
	  
/* 	  /\* FIRST SYNC *\/ */
	  
/* 	  /\* wait for i's signal *\/ */
/* 	  while(spe_out_mbox_status(spe[i]) == 0); */
/* 	  spe_out_mbox_read(spe[i], &recv_sig,1); */
/* 	  if (recv_sig != READY){ */
/* 	    perror("__PPE didn't receive a READY signal\n"); */
/* 	    exit(1); */
/* 	  } */
/* 	  /\* send go signal to i-1 *\/ */
/* 	  while(spe_in_mbox_status(spe[(i-1+NUM_SPE)%NUM_SPE]) == 0); */
/* 	  spe_in_mbox_write(spe[(i-1+NUM_SPE)%NUM_SPE], &send_sig,1,SPE_MBOX_ALL_BLOCKING); */
	  
/* 	} */
/*       } */
/*     } */
/*   } */

  /*  ATTENTE FIN THREADS, LIBERATION RESSOURCES  */
  wait_free(thread,spe,prog);

  return 0;
}
  
